﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using OpenLS.Spreadsheet.Formats;
using OpenLS.Spreadsheet.Formats.JsonStructures;
using QuantumWorkspace.SessionManager.DataLayer;
using QuantumWorkspace.SessionManager.Interfaces;
using Raven.Imports.Newtonsoft.Json;
using Roslyn.Scripting;
using Roslyn.Scripting.CSharp;

namespace QuantumWorkspace.SessionManager
{
    public class CodeSession
    {
        private static CodeSession _Instance;
        private BigDataTableDAL bigDataControler;
        private ScriptEngine _engine;
        private Session _session;

        private static List<string> _GeneratedTypesFromTableName; 

        private JsonTableDocument _declaredTableDocument;
        private JsonTableDocument DeclaredTableDocument
        {
            get { return _declaredTableDocument; }
            set { _declaredTableDocument = value; }
        }

        private CodeSession()
        {
            _GeneratedTypesFromTableName = new List<string>();
            bigDataControler = new BigDataTableDAL();
            _engine = new ScriptEngine();
            _session = _engine.CreateSession(this);
            AddReferences();
            
        }

        private void AddReferences()
        {
            new[]
                {
                    typeof (Console).Assembly, typeof(System.Collections.ObjectModel.ObservableCollection<>).Assembly,
                    typeof (IDictionary).Assembly, typeof (IEnumerable<>).Assembly, typeof (IQueryable).Assembly, this.GetType().Assembly, typeof (INotifyPropertyChanged).Assembly
                }.ToList().ForEach(asm => _engine.AddReference(asm)); new[] { "System", "System.Linq", "System.Collections", "System.Collections.Generic", "System.ComponentModel", "System.Collections.ObjectModel" }.ToList().ForEach(ns => _engine.ImportNamespace(ns));

            new[]
                {
                    typeof (Console).Assembly, 
                    typeof (IDictionary).Assembly, typeof (IEnumerable<>).Assembly, typeof (IQueryable).Assembly, this.GetType().Assembly, typeof (INotifyPropertyChanged).Assembly
                }.ToList().ForEach(asm => _session.AddReference(asm)); new[] { "System", "System.Linq", "System.Collections", "System.Collections.Generic", "System.ComponentModel", "System.Collections.ObjectModel" }.ToList().ForEach(ns => _session.ImportNamespace(ns)); 



            
        }

        public object Execute(string code)
        {
            return _session.Execute(code);
        }

        public void ImportNamespace(string ns)
        {
            _session.ImportNamespace(ns);
        }

        public static CodeSession Instance
        {
            get
            {
                if (_Instance == null)
                    _Instance = new CodeSession();
                return _Instance;
            }
        }

     

        // TODO SJE: Fix ClassGenerator
        private string ClassGenerator()
        {

            string strClass = "using System; using System.ComponentModel;";
            // strClass += "using QuantumWorkspace.SessionManager.DataLayer;";
            strClass += "public class " + DeclaredTableDocument.CurrentTable.Name + " :INotifyPropertyChanged ";
            strClass += "{";
            strClass += "public "+DeclaredTableDocument.CurrentTable.Name+"(){} ";
            int colCount = 0;
            foreach (var aField in DeclaredTableDocument.CurrentTable.TableFields)
            {
                // strClass += @"[BigDataTableColumnLabel(""" + aField.Id + @""""")] ";
                
                switch (aField.Type)
                {
                    case "number":
                        strClass += "public double Col" + colCount + " { get; set; } ";
                        break;
                    case "date":
                        strClass += "public DateTime Col" + colCount + " { get; set; } ";
                        break;
                    case "string":
                        strClass += "public string Col" + colCount + " { get; set; } ";
                        break;
                    default:
                        strClass += "public string Col" + colCount + " { get; set; } ";
                        break;
                }
                colCount++;
            }
            strClass += InotifyPropertyChangedMethof();
            strClass += "}";
            return strClass;
        }


        private string InotifyPropertyChangedMethof()
        {
            
            string strNotifMethod = "private void NotifyPropertyChanged(string propertyName)";
            strNotifMethod += "{";
            strNotifMethod += "         PropertyChangedEventHandler handler = PropertyChanged; ";
            strNotifMethod += "          if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));";
            strNotifMethod += "} ";
            strNotifMethod += "public event PropertyChangedEventHandler PropertyChanged;";
            return strNotifMethod;
        }


        // TODO SJE: Fix GetGridRowContentMethodGenerator
        public string GetGridRowContentMethodGenerator(JsonDataTableFormat JsonTableDefinition)
        {
            string strMethod = "ObservableCollection<object> GetDataGridContent()";
            strMethod += " { ";
            strMethod +=
                "ObservableCollection<object> aTestList = new ObservableCollection<object>();";

            for (int indexer = 0; indexer < JsonTableDefinition.NumberOfRows; indexer++)
            {
                var currentRow = JsonTableDefinition.GetRow(indexer);

                string strNewRowItemName = " aNewRowItem" + indexer;
                strMethod += GetNewRowItemSourceCode(strNewRowItemName, JsonTableDefinition, currentRow);
                strMethod += "aTestList.Add(" + strNewRowItemName + ");";
            }

            strMethod += "return aTestList;";
            strMethod += "}";

            return strMethod;
        }

        private string GetNewRowItemFunction(JsonDataTableFormat JsonTableDefinition)
        {
            string strMethod = DeclaredTableDocument.CurrentTable.Name+" GetNewGridRow()";
            strMethod += "{";

            List<object> newTableRow = new List<object>();
            Random aRandomiser = new Random(DateTime.Now.Second);
            foreach (var aField in DeclaredTableDocument.CurrentTable.TableFields)
            {
                switch (aField.Type)
                {
                    case "number":
                        newTableRow.Add(aRandomiser.Next(2000));
                        break;
                    case "date":
                        newTableRow.Add(DateTime.Now.AddHours(aRandomiser.Next(10000)));
                        break;
                    case "string":
                        newTableRow.Add("Random string " + aRandomiser.Next(2000));
                        break;
                    default:
                        newTableRow.Add("Random string " + aRandomiser.Next(2000));
                        break;
                }
            }

            strMethod += GetNewRowItemSourceCode("NewRowItem", JsonTableDefinition, newTableRow);
            strMethod += "return NewRowItem;";
            strMethod += "}";
            return strMethod;
        }

        private string GetNewRowItemSourceCode(string strNewRowItemName, JsonDataTableFormat JsonTableDefinition, List<object> rowData)
        {
            string strMethod = DeclaredTableDocument.CurrentTable.Name + " " + strNewRowItemName + " = new " +
                             DeclaredTableDocument.CurrentTable.Name + "();";


            for (int colIndex = 0; colIndex < JsonTableDefinition.NumberOfColumns; colIndex++)
            {
                switch (DeclaredTableDocument.CurrentTable.TableFields[colIndex].Type)
                {
                    case "number":
                        if (rowData[colIndex] == null)
                            strMethod += strNewRowItemName + ".Col" + colIndex + @"= 0;";
                        else
                            strMethod += strNewRowItemName + ".Col" + colIndex + "=" + Convert.ToString(rowData[colIndex]) + ";";
                        break;
                    case "date":
                        strMethod += strNewRowItemName + ".Col" + colIndex + @"= DateTime.Parse(""" + Convert.ToString(rowData[colIndex]) + @""",System.Globalization.CultureInfo.InvariantCulture);";
                        break;
                    case "string":
                        strMethod += strNewRowItemName + ".Col" + colIndex + @"=""" + Convert.ToString(rowData[colIndex]) + @""";";
                        break;
                    default:
                        if (rowData[colIndex] == null)
                            strMethod += strNewRowItemName + ".Col" + colIndex + @"= """";";
                        else
                            strMethod += strNewRowItemName + ".Col" + colIndex + "=" + Convert.ToString(rowData[colIndex]) + ";";
                        break;
                }

            }
            return strMethod;
        }

        public string ColumnInfoProvider()
        {
            string strMethod = "Dictionary<string,string> GetColumnInfoProvider()";
            strMethod += "{";
            strMethod += "      Dictionary<string,string> columnMapping = new Dictionary<string,string>();";
            int colCount = 0;
            foreach (var aField in DeclaredTableDocument.CurrentTable.TableFields)
            {
                strMethod += @"      columnMapping.Add(""Col"+colCount+@""", """+aField.Id+@""");";
                colCount++;
            }
            strMethod += "      return columnMapping;";
            strMethod += "}";
            return strMethod;
        }

        public Dictionary<string, string> GetDataGridColumnsNames()
        {
            _session.Execute(ColumnInfoProvider());
            object columnInfo = _session.Execute("GetColumnInfoProvider();");
            return ((Dictionary<string, string>) columnInfo);
        }

        public INotifyPropertyChanged GetNewRowItem(JsonDataTableFormat JsonTableDefinition)
        {
            JsonTableDocument tableDefinition = JsonConvert.DeserializeObject<JsonTableDocument>(JsonTableDefinition.TableDescription);
            DeclaredTableDocument = tableDefinition;
            var dataTable = bigDataControler.GetOrCreate(tableDefinition);
            if (!_GeneratedTypesFromTableName.Contains(tableDefinition.CurrentTable.Name))
            {
                _session.Execute(ClassGenerator());
                _GeneratedTypesFromTableName.Add(tableDefinition.CurrentTable.Name);
            }
            _session.Execute(GetNewRowItemFunction(JsonTableDefinition));
            var returnObject = (INotifyPropertyChanged)_session.Execute("GetNewGridRow();");
            return returnObject;
        }

        public ObservableCollection<object> GetDataGridRowsExternal(JsonDataTableFormat JsonTableDefinition)
        {
            JsonTableDocument tableDefinition = JsonConvert.DeserializeObject<JsonTableDocument>(JsonTableDefinition.TableDescription);
            DeclaredTableDocument = tableDefinition;
            var dataTable = bigDataControler.GetOrCreate(tableDefinition);
            if (!_GeneratedTypesFromTableName.Contains(tableDefinition.CurrentTable.Name))
            {
                _session.Execute(ClassGenerator());
                _GeneratedTypesFromTableName.Add(tableDefinition.CurrentTable.Name);
            }
            
            _session.Execute(GetGridRowContentMethodGenerator(JsonTableDefinition));
            var returnObject = (IEnumerable)_session.Execute("GetDataGridContent();");
            return (ObservableCollection<object>)returnObject;
            /*
            this.TableName = tableDefinition.CurrentTable.Name;
            foreach (var aTableColumn in tableDefinition.CurrentTable.TableFields)
            {
                GridColumn dynamicColumn = new GridColumn();
                if (aTableColumn.Type == "date")
                    dynamicColumn.EditSettings = new DateEditSettings();
                else
                    dynamicColumn.EditSettings = new TextEditSettings();

                dynamicColumn.Header = aTableColumn.Id;
                // dynamicColumn. = aTableColumn.Type;
                this.dynamicTableGrid.Columns.Add(dynamicColumn);
            }
            */
        }
    }
}
